package ssq.gamest.wolfmankill;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import net.sf.json.JSONObject;
import ssq.gamest.wolfmankill.Participant.Role;
import ssq.gamest.wolfmankill.Participant.Status;
import ssq.utils.Base64Utils;
import ssq.utils.LogUtils;
import ssq.utils.StringUtils;

public class WSServer extends WebSocketServer {
  private static final String SYSTEM_MSG = "系统消息";
  private static final String GOD = "上帝";
  private static final Participant none = new Participant("none");

  List<Participant> participants = new LinkedList<Participant>();

  List<Participant> voters = new LinkedList<Participant>();
  List<String> votees = new LinkedList<String>();
  HashMap<String, String> voteHistory = new HashMap<String, String>();
  int voteSize;
  int cntOfDead;
  private Participant lastProtected;

  private Participant lastKilled;
  private Participant lastPoisoned;
  private LinkedList<Participant> diedLastNight;
  private Participant sergent;
  private boolean blamedTheElder;
  private int round;
  private int cntOfWolves;
  private State state;
  private boolean poisonAvailable;
  private boolean antiDoteAvailable;

  public String toDetailedString() {
    StringBuilder sb = new StringBuilder(participants.toString());
    sb.append("\nlovers:").append(Arrays.asList(lover)).append("\ndiedLastNight").append(diedLastNight).append("\nround: ").append(round).append("\ncntOfDead: ").append(cntOfDead).append("\ncntOfWolves: ").append(cntOfWolves).append("\nstate: ").append(state).append("\nvoters : ").append(voters.toString()).append("\nvotees: ").append(votees.toString()).append("\nvoteHistory: ").append(voteHistory.toString()).append("\nvoteSize: ").append(voteSize);
    return sb.toString();
  }

  private void iniGame() {
    sergent = none;
    blamedTheElder = false;
    round = 0;
    poisonAvailable = true;
    antiDoteAvailable = true;
    cntOfDead = 0;
    cntOfWolves = 0;
    for (Role role : roles) {
      if (role == Role.狼人) {
        cntOfWolves++;
      }
    }
    lastProtected = none;

    participants = participants.subList(0, playerNum);
  }

  private void iniRound() {
    round++;
    lastKilled = none;
    lastPoisoned = none;
    diedLastNight = new LinkedList<Participant>();
  }

  Participant[] lover = new Participant[2];
  LinkedList<Role> roles = new LinkedList<Participant.Role>();
  int playerNum;
  private boolean killSide;

  enum State {
    wait, gamebegin,

    shuffle, 盗贼, 丘比特,

    roundbegin, 狼人, 预言家, 守卫, 女巫, dawn, apply, dawn2, day, vote, roundend, gameend
  }

  private void next() {
    if (state == State.gameend) {
      state = State.wait;
    } else if (state == State.roundend) {
      state = State.roundbegin;
    } else {
      state = State.values()[state.ordinal() + 1];

      if (state == State.apply && round != 1)
        next();

      try {
        Role role = Role.valueOf(state.toString());

        if (!roles.contains(role)) {
          next();
        }
      } catch (Exception e) {
      }

      if (gameOver()) {
        state = State.gameend;
      }
    }
  }

  private boolean gameOver() {
    if (state.ordinal() <= State.roundbegin.ordinal()) {
      return false;
    }

    boolean gameEnd = false;

    if (lover[0] != null && lover[0].status == Status.alive && (isGoodMan(lover[0].role) ^ isGoodMan(lover[0].role))) //如果人狼恋还活着
    {
      if (getAliveParticipants().size() == 2) {
        sendMsgToAll("人狼恋, 恋人获胜: " + lover[0] + ", " + lover[1]);
        gameEnd = true;
      }
    } else {
      boolean civilianDieOut = true;
      boolean godDieOut = true;
      for (Participant participant : getAliveParticipants()) {
        if (participant.role == Role.平民 || participant.role == Role.猎人 || participant.role == Role.丘比特 || participant.role == Role.长老) {
          civilianDieOut = false;
        } else if (participant.role != Role.狼人) {
          godDieOut = false;
        }
      }

      if ((civilianDieOut || godDieOut) && killSide || civilianDieOut && godDieOut) {
        sendMsgToAll("狼人获胜");
        gameEnd = true;
      }

      boolean wereWolfDieOut = true;
      for (Participant participant : getAliveParticipants()) {
        if (!isGoodMan(participant.role)) {
          wereWolfDieOut = false;
          break;
        }
      }
      if (wereWolfDieOut) {
        sendMsgToAll("好人获胜");
        gameEnd = true;
      }
    }

    return gameEnd;
  }

  abstract class Conditioner {
    abstract boolean condition();
  }

  private void setAllAlivedVotable() {
    for (Participant participant : getAliveParticipants()) {
      votees.add(participant.toString());
    }
  }

  private void checkIfBlamedTheElder(Participant tmp) {
    if (tmp.role == Role.长老) {
      blamedTheElder = true;
      sendInfoToPartial("长老被冤死!!!", getAliveParticipants());
    }
  }

  private void checkLover(Participant toKill) {
    int i = 0;
    if (lover[i++] == toKill || lover[i--] == toKill) {
      sendMsgToAll(lover[i] + "和" + toKill + "是情侣, 被带死. ");
      kill(lover[i]);
    }
  }

  private void sendInfoToPartial(String msg, LinkedList<Participant> aliveParticipants) {
    sendInfoToPartial(msg, participants.toArray(new Participant[] {}));
  }

  private void schedule() {
    while (true) {
      work();
    }
  }

  private void work() {
    switch (state) {
    case wait: {
      stage("等待玩家加入", new Runnable() {
        @Override
        public void run() {
          waitAside(new Conditioner() {
            @Override
            public boolean condition() {
              return participants.size() < playerNum;
            }
          });
        }
      });
      break;
    }

    case gamebegin: {
      stage("人数已凑齐, 游戏马上开始", new Runnable() {
        @Override
        public void run() {
          iniGame();
        }
      });
      break;
    }

    case shuffle: {
      stage("正在排座次和身份...", new Runnable() {
        @Override
        public void run() {
          Collections.shuffle(participants);
          Collections.shuffle(roles);
          sendToAll(constructMessage("players", StringUtils.join(",", participants) + ",none"));

          for (int i = 0; i < participants.size(); ++i) {
            Participant p = participants.get(i);
            p.setRole(roles.get(i));
            p.setStatus(Status.alive);
            sendMsgToPartial("你的身份是" + p.getRole(), p);
            sendToPartial(constructMessage("fromJson", p.toJson()), p);
          }

          sleepAWhile();
          sendMsgToAll("所有人的身份已经各自通知完毕");
          sendToAll(constructMessage("disable", "none"));
        }
      });
      break;
    }

    case 盗贼: {
      stage("盗贼换牌阶段", new Runnable() {
        @Override
        public void run() {
          final Participant robber = getAliveParticipants(Role.盗贼).get(0);
          voters.add(robber);

          List<Role> tmp = roles.subList(playerNum, roles.size());
          tmp.add(Role.盗贼);

          for (Role role : tmp) {
            votees.add(role.toString());
          }
          voteSize = 1;

          sendToPartial(constructMessage("players", StringUtils.join(",", votees)), robber);

          sendShiverToPartial("用鼠标选一张牌", robber);
          waitForVotes();

          String result = voteHistory.values().toArray(new String[] {})[0];
          robber.setRole(Role.valueOf(result));
          sendShiverToPartial("你的身份变为" + result, robber);

          sendToPartial(constructMessage("fromJson", robber.toJson()), robber);
          sendToPartial(constructMessage("players", StringUtils.join(",", participants) + ",none"), robber);
          sendToPartial(constructMessage("disable", "none"), robber);
        }
      });
      break;
    }

    case 丘比特: {
      stage("丘比特指定情侣阶段", new Runnable() {
        @Override
        public void run() {
          final Participant cupid = getAliveParticipants(Role.丘比特).get(0);
          sendShiverToPartial("用鼠标指定两个情侣", cupid);
          voters.add(cupid);
          setAllAlivedVotable();
          voteSize = 2;

          waitForVotes();
          sendToPartial(constructMessage("toggleVote", ""), cupid);

          Object[] tmp = voteHistory.values().toArray();

          lover[0] = getParticipant(tmp[0].toString());
          lover[1] = getParticipant(tmp[1].toString());

          sendMsgToPartial("你选定的恋人是" + tmp[0] + "和" + tmp[1], cupid);

          sendShiverToPartial(lover[0] + "是你的恋人, 他的身份是" + lover[0].role, lover[1]);
          sendShiverToPartial(lover[1] + "是你的恋人, 他的身份是" + lover[1].role, lover[0]);
        }
      });
      break;
    }

    case roundbegin: {
      stage("天黑请闭眼", new Runnable() {
        @Override
        public void run() {
          iniRound();
        }
      });
      break;
    }

    case 预言家: {
      stage("预言家请验人", new Runnable() {
        @Override
        public void run() {
          Participant predictor = getAliveParticipants(Role.预言家).get(0);

          Participant predicted = waitForOneVotee("请用鼠标指向一个要验的人", predictor);
          sendShiverToPartial(predicted + "是" + (predicted.role.isGoodMan() ? "好人" : "坏人"), predictor);
        }
      });
      break;
    }

    case 守卫: {
      stage("守护者请守护", new Runnable() {
        @Override
        public void run() {
          Participant protector = getAliveParticipants(Role.守卫).get(0);

          if (lastProtected != none) {
            votees.remove(lastProtected);
            sendToPartial(constructMessage("disable", lastProtected.name), protector);
          }

          Participant protectedMan = waitForOneVotee("请用鼠标指向一个要守护的人", protector);

          if (lastProtected.status != Status.dead) {
            sendToPartial(constructMessage("enable", lastProtected.name), protector);
          }

          sendShiverToPartial(protectedMan + "此轮不会被狼杀死", protector);
          lastProtected = protectedMan;
        }
      });
      break;
    }

    case 狼人: {
      stage("狼人请杀人", new Runnable() {
        @Override
        public void run() {
          List<Participant> wolves = getAliveParticipants(Role.狼人);
          sendShiverToPartial(wolves + "是狼人. 用鼠标投票, 请统一意见", wolves);
          voters = wolves;
          setAllAlivedVotable();
          voteSize = voters.size();

          sendToggleVote();
          waitAside(new Conditioner() {
            @Override
            boolean condition() {
              String choice = null;
              for (String val : voteHistory.values()) {
                if (choice == null) {
                  choice = val;
                } else if (!choice.equals(val)) {
                  return true;
                }
              }

              return voteSize != voteHistory.size();
            }
          });
          sendToggleVote();

          lastKilled = getParticipant(voteHistory.values().toArray(new String[] {})[0]);
          sendMsgToPartial("你们杀了" + lastKilled, wolves);

          if (lastProtected.equals(lastKilled)) {
            lastKilled = none;
          }
          sendToPartial(constructHighlight(""), wolves);
        }
      });
      break;
    }

    case 女巫: {
      stage("女巫救人毒人阶段", new Runnable() {
        @Override
        public void run() {
          Participant witch = getAliveParticipants(Role.女巫).get(0);
          sendShiverToPartial("今天晚上" + (lastKilled == none ? "没有人死" : lastKilled + "死了"), witch);
          boolean savedThisNight = false;

          if (lastKilled != none && antiDoteAvailable) {
            voters.add(witch);
            voteSize = 1;
            votees.add(none.name);
            votees.add(lastKilled.name);

            sendShiverToPartial("你可以选择救他或者放弃", witch);
            sendToPartial(constructMessage("disable", StringUtils.join(",", getParticipants())), witch);
            sendToPartial(constructMessage("enable", "none"), witch);
            sendToPartial(constructMessage("enable", lastKilled.name), witch);

            Participant saved = getMaxVoted();

            if (saved == lastKilled) {
              savedThisNight = true;
              sendShiverToPartial("你救起了" + saved, witch);
              lastKilled = none;
              antiDoteAvailable = false;
            }

            sendToPartial(constructMessage("enable", "all"), witch);
            LinkedList<Participant> tmp = new LinkedList<Participant>(getDeadParticipants());
            tmp.addFirst(none);
            sendToPartial(constructMessage("disable", StringUtils.join(",", tmp)), witch);

            clearVote();
          } else {
            sendShiverToPartial("你的解药已用完或今天没有人死, 暂停一个随机时间. ", witch);
            sleepARandomTime();
          }

          if (poisonAvailable && !savedThisNight) {
            voters.add(witch);
            voteSize = 1;
            setAllAlivedVotable();
            votees.add("none");

            sendToPartial(constructMessage("enable", "none"), witch);
            sendShiverToPartial("请选择想毒死的人, 不毒就选择放弃", witch);

            lastPoisoned = getMaxVoted();

            if (lastPoisoned != none) {
              sendShiverToPartial("你毒死了" + lastPoisoned, witch);
              poisonAvailable = false;

              checkIfBlamedTheElder(lastPoisoned);
            }

            sendToPartial(constructMessage("disable", "none"), witch);
          } else {
            sendShiverToPartial("你的毒药用完或今天使用了解药, 暂停一个随机时间. ", witch);
            sleepARandomTime();
          }
        }
      });
      break;
    }

    case dawn: {
      stage("天亮了. ", new Runnable() {
        @Override
        public void run() {
          if (lastKilled != none) {
            if (lastKilled.role == Role.长老 && lastKilled.status == Status.alive) {
              lastKilled.status = Status.half_alive;
            } else if (!diedLastNight.contains(lastKilled)) {
              diedLastNight.add(lastKilled);
            }
          }

          if (lastPoisoned != none && !diedLastNight.contains(lastPoisoned)) {
            diedLastNight.add(lastPoisoned);
          }

          for (Participant tmp : new LinkedList<Participant>(diedLastNight)) {
            int i = 0;
            if (lover[i++] == tmp || lover[i--] == tmp) {
              if (!diedLastNight.contains(lover[i])) {
                diedLastNight.add(lover[i]);
              }
            }
          }
          Collections.shuffle(diedLastNight);
        }
      });
      break;
    }

    case apply: {
      stage("竞选警长", new Runnable() {
        @Override
        public void run() {
          sendMsgToAll("大家自由发言并用鼠标投票");
          voters.addAll(getAliveParticipants());
          voteSize = getAliveParticipants().size();
          setAllAlivedVotable();

          sergent = getMaxVoted(true);

          sendMsgToAll(sergent + "当选为新警长");
          sergent.isSergent = true;
          sendToPartial(constructMessage("fromJson", sergent.toJson()), sergent);
        }
      });
      break;
    }

    case dawn2: {
      stage((diedLastNight.isEmpty() ? "平安夜" : (StringUtils.join(",", diedLastNight)) + "在昨晚死了. "), new Runnable() {
        @Override
        public void run() {
          int cnt = 0;
          for (Participant participant : diedLastNight) {
            if (cntOfDead + cnt++ > cntOfWolves) {
              sendMsgToAll(participant + "没有遗言");
            } else {
              voters.add(participant);
              voteSize = 1;
              votees.add("any");
              sendMsgToAll(participant + "遗言阶段");
              sendQueryInfoToPartial("请说遗言", "vote", participant);

              waitForVotes();

              sendMsgToAll(voteHistory.values().toArray()[0].toString(), participant.name, "遗言");

              clearVote();
            }

            kill(participant);
          }
        }
      });
      break;
    }

    case day: {
      stage("发言阶段", new Runnable() {
        @Override
        public void run() {
          Participant first = diedLastNight.isEmpty() ? participants.get(0) : diedLastNight.get(0);

          if (sergent != none) {
            sendMsgToAll("警长组织发言");
            sendShiverToPartial("选定首个发言的人, 然后依次发言", sergent);

            voters.add(sergent);
            setAllAlivedVotable();
            voteSize = 1;

            first = getMaxVoted();
            clearVote();
          }

          int i = 0;
          for (Iterator<Participant> iterator = participants.iterator(); iterator.hasNext(); ++i) {
            Participant tmp = iterator.next();
            if (tmp == first) {
              break;
            }
          }

          LinkedList<Participant> addressers = new LinkedList<Participant>(participants.subList(i, participants.size()));
          addressers.addAll(participants.subList(0, i));

          for (Participant addresser : addressers) {
            if (addresser.status != Status.dead) {
              voters.add(addresser);
              voteSize = 1;
              votees.add("any");

              sendMsgToAll(addresser + "发言");
              sendQueryInfoToPartial("请发言", "vote", addresser);
              waitForVotes();

              sendMsgToAll(voteHistory.values().toArray()[0].toString(), addresser.name, "发言");
              clearVote();
            }
          }
        }
      });
      break;
    }

    case vote: {
      stage("请大家投票, 把你的小手伸出来, 指向你心中的狼人! ", new Runnable() {
        @Override
        public void run() {
          voters.addAll(getAliveParticipants());
          setAllAlivedVotable();
          voteSize = voters.size();

          Participant tmp = getMaxVoted(true);

          sendMsgToAll(tmp + "得票最多, 现在行刑!");
          checkIfBlamedTheElder(tmp);
          kill(tmp);

          checkLover(tmp);
        }
      });
      break;
    }

    case roundend: {
      stage("淫荡的一天结束了", new Runnable() {
        @Override
        public void run() {
        }
      });
      break;
    }

    case gameend: {
      StringBuilder sb = new StringBuilder("游戏结束, 所有人的身份如下: ");
      for (Participant participant : participants) {
        sb.append("\n***********\n").append(participant.toFullString()).append("\n***********\n");
      }

      stage(sb.toString(), new Runnable() {
        @Override
        public void run() {
          closeAll();
          participants.clear();
        }
      });
      break;
    }

    default:
      break;
    }
  }

  private void closeAll() {
    for (Iterator<Participant> iterator = participants.iterator(); iterator.hasNext();) {
      Participant participant = iterator.next();
      participant.getSocket().close();
    }
  }

  protected LinkedList<Participant> getDeadParticipants() {
    LinkedList<Participant> tmp = new LinkedList<Participant>(participants);
    tmp.removeAll(getAliveParticipants());
    return tmp;
  }

  protected void kill(Participant toKill) {
    if (toKill == none) {
      return;
    }

    LogUtils.logString(toKill.toFullString(), "kill", true);

    cntOfDead++;
    toKill.status = Status.dead;

    if (sergent == toKill) {
      handOverSergent();
    }

    if (toKill.getRole() == Role.猎人) {
      hunt(toKill);
    }

    sendToPartial(constructMessage("disable", "all"), toKill);
    sendShiverToPartial("你死了", toKill);
    sendToAll(constructMessage("disable", toKill.name));
  }

  private void hunt(Participant hunter) {
    clearVote();
    sendShiverToPartial("你死了, 请开始打猎", hunter);
    voters = new LinkedList<>(Arrays.asList(new Participant[] { hunter }));
    setAllAlivedVotable();
    voteSize = voters.size();

    sendToggleVote();
    waitAside(new Conditioner() {
      @Override
      boolean condition() {
        String choice = null;
        for (String val : voteHistory.values()) {
          if (choice == null) {
            choice = val;
          } else if (!choice.equals(val)) {
            return true;
          }
        }

        return voteSize != voteHistory.size();
      }
    });
    sendToggleVote();

    lastKilled = getParticipant(voteHistory.values().toArray(new String[] {})[0]);

    if (lastKilled.getName() == "none") {
      return;
    }

    sendMsgToPartial("你猎杀了" + lastKilled, hunter);

    kill(lastKilled);
    checkIfBlamedTheElder(lastKilled);
    checkLover(lastKilled);
  }

  private void waitAside(final Conditioner conditioner) {
    while (conditioner.condition()) {
      sleepAWhile();
    }
  }

  private void stage(String info, Runnable runnable) {
    if (blamedTheElder && isGoodMensTurn()) {
      sendMsgToAll(info + "\n******长老已被冤死, 所有特技失效******");
      sleepAWhile();
      sleepAWhile();
      sleepAWhile();

    } else {
      sendMsgToAll(info);
      sleepAWhile();
      sleepAWhile();
      sleepAWhile();

      try {
        runnable.run();
      } catch (Exception e) {
        if (!(e instanceof IndexOutOfBoundsException)) {
          e.printStackTrace();
        }

        try {
          Thread.sleep(7000 + new Random().nextInt(5000));
        } catch (InterruptedException e1) {
        }
      }

      clearVote();
    }

    next();
  }

  private boolean isGoodMensTurn() {
    try {
      return isGoodMan(Role.valueOf(state.toString()));
    } catch (Exception e) {
      return false;
    }
  }

  public boolean isGoodMan(Role role) {
    return role != Role.狼人;
  }

  private void clearVote() {
    voteSize = 0;
    voteHistory.clear();
    voters.clear();
    votees.clear();

    for (Participant participant : participants) {
      participant.resetVoteCnt();
    }
  }

  private void sendShiverToPartial(String msg, List<Participant> receivers) {
    sendShiverToPartial(msg, receivers.toArray(new Participant[] {}));
  }

  private void sendShiverToPartial(String msg, Participant... receivers) {
    sendToPartial(constructMessage("shiver", "[振动]: " + msg), receivers);
  }

  public static String constructMessage(String type, String msg) {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("type", type);
    jsonObject.put("msg", msg);
    return jsonObject.toString();
  }

  private void sendMsgToAll(String msg) {
    sendMsgToAll(msg, GOD, SYSTEM_MSG);
  }

  public WSServer(LinkedList<Role> roles, int playerNum, boolean killSide, int port) {
    super(new InetSocketAddress(port), 10);
    this.playerNum = playerNum;
    this.roles = roles;
    this.killSide = killSide;
    //    new DebugFrame(this);
  }

  @Override
  public void onClose(WebSocket arg0, int arg1, String arg2, boolean arg3) {
    System.out.println("[" + arg0.getLocalSocketAddress() + "] existed!");

    for (Participant participant : participants) {
      if (participant.getSocket().equals(arg0)) {
        participants.remove(participant); //bad efficiency
        return;
      }
    }
  }

  @Override
  public void onError(WebSocket arg0, Exception arg1) {
    System.err.println("\n******************\n" + (arg0 != null ? arg0.getLocalSocketAddress() : "") + "\n" + arg1.getLocalizedMessage() + "\n**************************************");
    arg1.printStackTrace();
  }

  public List<Participant> getParticipants() {
    return participants;
  }

  public List<Participant> getParticipants(Status status) {
    LinkedList<Participant> result = new LinkedList<Participant>();
    for (Participant participant : result) {
      if (participant.getStatus().equals(status)) {
        result.add(participant);
      }
    }
    return result;
  }

  public List<Participant> getAliveParticipants(Role role) {
    LinkedList<Participant> result = new LinkedList<Participant>();
    for (Participant participant : participants) {
      if (participant.getRole().equals(role) && participant.status != Status.dead) {
        result.add(participant);
      }
    }
    return result;
  }

  public Participant getParticipant(WebSocket webSocket) {
    for (Participant participant : participants) {
      if (participant.getSocket().equals(webSocket)) {
        return participant;
      }
    }
    return null;
  }

  public Participant getParticipant(String name) {
    for (Participant participant : participants) {
      if (participant.getName().equals(name)) {
        return participant;
      }
    }
    return none;
  }

  LinkedList<Participant> getAliveParticipants() {
    LinkedList<Participant> result = new LinkedList<Participant>();
    for (Participant participant : participants) {
      if (participant.status != Status.dead) {
        result.add(participant);
      }
    }

    return result;
  }

  private Participant getMaxVoted() {
    return getMaxVoted(false);
  }

  /**
   * 已经配置好首次投票的候选者, 如果出现票数一样, 自动继续投票.
   */
  private Participant getMaxVoted(boolean openResult) {
    while (voters.size() > 0) {
      waitForVotes();

      if (openResult) {
        sendMsgToAll("投票结果:\n" + voteHistory);
      }

      LinkedList<Participant> maxVoted = new LinkedList<Participant>();

      for (Participant tmp : getAliveParticipants()) {
        if (maxVoted.size() == 0) {
          if (tmp.voteCnt != 0) {
            maxVoted.add(tmp);
          }
        } else if (maxVoted.get(0).voteCnt > tmp.voteCnt) {
        } else if (maxVoted.get(0).voteCnt < tmp.voteCnt) {
          maxVoted.clear();
          maxVoted.add(tmp);
        } else {
          maxVoted.add(tmp);
        }
      }

      if (maxVoted.isEmpty()) {
        return none;
      } else if (maxVoted.size() == 1) {
        return maxVoted.get(0);
      } else {
        voters.removeAll(maxVoted);
        votees.clear();
        voteHistory.clear();
        voteSize = voters.size();

        for (Participant participant : maxVoted) {
          votees.add(participant.name);
        }

        sendMsgToAll(votees + "得票数一样多, 都是" + maxVoted.get(0).voteCnt + "票. 请继续在他们之间投票! ");
      }
    }

    return none;
  }

  @Override
  public void onMessage(WebSocket socket, String message) {
    //        System.out.println("in: " + message);

    JSONObject jsonObject = JSONObject.fromObject(message);
    String msg = jsonObject.getString("msg");

    Participant participant = none;
    try {
      participant = getParticipant(socket);
    } catch (Exception e) {
    }

    String type = jsonObject.getString("type");

    if (type.equals("register")) {
      if (participants.size() == playerNum) {
        socket.send(constructInfo("此服务器人数已满"));
        socket.close();
        return;
      }

      String name = msg.trim();

      if (name.length() > 8) {
        socket.send(constructInfo("名字太长"));
        socket.close();
        return;
      }

      boolean occupied = false;

      if (reserved(name)) {
        occupied = true;
      } else {
        for (Participant p : participants) {
          if (p.getName().equals(name)) {
            if (!p.getSocket().equals(socket)) {
              occupied = true;
              break;
            }
          }
        }
      }

      if (!occupied) {
        participants.add(new Participant(socket, name));
        sendMsgToAll(name + " 被分配给 " + socket.getRemoteSocketAddress());
      } else {
        socket.send(constructQueryInfo("名字已经占用, 请更改", "register"));
      }
    } else if (type.equals("msg")) {
      String receiver = "all";
      try {
        receiver = jsonObject.getString("to");
      } catch (Exception e) {
      }

      if (receiver.equals("all")) {
        sendMsgToAll(msg, participant.getName(), "群聊");
      } else {
        sendMsgToAll(participant + "向" + receiver + "发送了一条悄悄话");
        sendMsgToPartial(msg, participant.getName(), "私聊", getParticipant(receiver));
      }
    } else if (type.equals("vote")) {
      if (canVote(socket, msg)) {
        getParticipant(voteHistory.put(participant.name + (state == State.丘比特 ? voteHistory.size() : ""), msg)).voteCnt -= participant == sergent && state == State.vote ? 2 : 1;

        if (state != State.狼人 && state != State.丘比特) {
          sendToPartial(constructMessage("toggleVote", ""), participant);
        }

        if (state == State.apply || state == State.vote || state == State.丘比特) {
          sendMsgToPartial("你选了" + msg, participant);
        }

        try {
          Participant votedParticipant = getParticipant(msg);

          if (votedParticipant != null) {
            if (state == State.vote && participant == sergent) {
              votedParticipant.voteCnt += 2;
            } else {
              votedParticipant.voteCnt++;
            }
          }
        } catch (Exception e) {
        }

        informVoteChange();
      } else {
        socket.send(constructInfo("别乱投票"));
      }
    }
  }

  /**
   * 此方法假设你已设置好votees
   */
  private void handOverSergent() {
    sendMsgToAll("警长死了, 请移交警徽");
    sendShiverToPartial("用鼠标选择一个警徽接受者", sergent);
    clearVote();
    voters.add(sergent);
    voteSize = 1;
    setAllAlivedVotable();

    sergent = getMaxVoted();
    sendMsgToAll("新警长是" + sergent);
    sergent.isSergent = true;
    sendToPartial(constructMessage("fromJson", sergent.toJson()), sergent);
  }

  private void informVoteChange() {
    if (state == State.狼人) {
      sendToPartial(constructHighlight(StringUtils.join(",", voteHistory.entrySet())), getAliveParticipants(Role.狼人));
    }
  }

  private String constructHighlight(String msg) {
    return constructMessage("highlight", msg);
  }

  private boolean reserved(String name) {
    if (name.contains("[") || name.contains("]") || name.contains(",") || name.contains(":")) {
      return true;
    }

    for (Role role : Role.values()) {
      if (name.contains(role.toString())) {
        return true;
      }
    }

    for (String str : new String[] { "all", "none", "any", GOD }) {
      if (name.contains(str)) {
        return true;
      }
    }

    return false;
  }

  private boolean canCancellVote(WebSocket socket, String votee) {
    Participant participant;
    try {
      participant = getParticipant(socket);
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }

    try {
      return participant.role == Role.狼人 && state == State.狼人 && voteHistory.get(participant.name).equals(votee);
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  private boolean canVote(WebSocket socket, String votee) {
    Participant participant;
    try {
      participant = getParticipant(socket);
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }

    if (state == State.丘比特 && !voteHistory.isEmpty() && votee.equals(voteHistory.values().toArray()[0].toString())) {
      return false;
    } else if (state == State.狼人 && participant.role == Role.狼人) {
      return true;
    } else {
      return voters.contains(participant) && voteHistory.get(participant.name) == null && (votees.get(0).equals("any") || votees.contains(votee)) && voteHistory.size() < voteSize;
    }
  }

  private void sendToAll(String command) {
    LogUtils.logString(command, "to all: ", true);

    for (WebSocket socket : connections()) {
      socket.send(command);
    }
  }

  private void sendQueryInfoToPartial(String hint, String backType, Participant... participants) {
    sendToPartial(constructQueryInfo(hint, backType), participants);
  }

  private void sendMsgToAll(String msg, String from, String tag) {
    sendMsgToPartial(msg, from, tag, participants);
  }

  private void sendMsgToPartial(String msg, List<Participant> participants) {
    sendMsgToPartial(msg, participants.toArray(new Participant[] {}));
  }

  protected static void sendMsgToPartial(String msg, Participant... participants) {
    sendMsgToPartial(msg, GOD, SYSTEM_MSG, participants);
  }

  protected static void sendMsgToPartial(String msg, String from, String tag, Participant... participants) {
    msg = constructMsg(msg, "[" + tag + "] " + from);
    sendToPartial(msg, participants);
  }

  protected static void sendMsgToPartial(String msg, String from, String tag, List<Participant> participants) {
    sendMsgToPartial(msg, from, tag, participants.toArray(new Participant[] {}));
  }

  protected static void sendToPartial(String msg, List<Participant> participants) {
    sendToPartial(msg, participants.toArray(new Participant[] {}));
  }

  protected static void sendToPartial(String msg, Participant... participants) {
    System.out.println("out: " + msg + "\tto: " + Arrays.asList(participants));

    for (Participant participant : participants) {
      if (participant != none) {
        participant.socket.send(msg);
      }
    }
  }

  public static String constructMsg(String msg, String from) {
    StringBuilder sb = new StringBuilder();
    try {
      sb.append("{\"type\":\"msg\",\"from\":\"").append(from).append("\",\"msg\":\"").append(Base64Utils.encode(msg.getBytes("UTF-8"))).append("\"}");
    } catch (Exception e) {
    }

    return sb.toString();
  }

  protected static String constructQueryInfo(String hint, String backType) {
    return constructQueryInfo(hint, backType, GOD);
  }

  protected static String constructQueryInfo(String hint, String backType, String from) {
    StringBuilder sb = new StringBuilder();
    sb.append("{\"type\":\"queryInfo\",\"msg\":{\"hint\":\"").append(hint).append("\",\"backType\":\"").append(backType).append("\"}}");
    return sb.toString();
  }

  @Override
  public void onOpen(WebSocket socket, ClientHandshake arg1) {
    LogUtils.logString("[" + socket.getLocalSocketAddress() + "] entered!");
    socket.send(constructMsg("欢迎! ", GOD));
  }

  private void sendInfoToPartial(String msg, Participant... participants) {
    sendToPartial(constructInfo(msg), participants);
  }

  private String constructInfo(String msg) {
    StringBuilder sb = new StringBuilder();
    sb.append("{\"type\":\"info\",\"msg\":\"").append(msg).append("\"}");
    return sb.toString();
  }

  @Override
  public void start() {
    state = State.wait;
    new Thread() {
      @Override
      public void run() {
        schedule();
      }
    }.start();

    super.start();

  }

  //狼人,狼人,女巫,预言家,守卫,盗贼,长老,丘比特,平民,平民,猎人  8 屠边   44343
  public static void main(String[] args) throws IOException, InterruptedException {
    runAndGetServer(args);
  }

  public static void sleepAWhile() {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
  }

  private void sleepARandomTime() {
    for (int i = 3 + new Random().nextInt(20); i > 0; --i) {
      sleepAWhile();
    }
  }

  public void waitForVotes() {
    sendToggleVote();
    waitAside(new Conditioner() {
      @Override
      boolean condition() {
        return voteHistory.size() < voteSize;
      }
    });
  }

  public void sendToggleVote() {
    sendToPartial(constructMessage("toggleVote", ""), voters);
  }

  private Participant waitForOneVotee(String hint, Participant 预言家) {
    sendShiverToPartial(hint, 预言家);
    voters.add(预言家);
    setAllAlivedVotable();
    voteSize = 1;

    Participant predicted = getMaxVoted();
    return predicted;
  }

  public static WSServer runAndGetServer(String[] args) {
    LogUtils.logString(Arrays.asList(args).toString(), "server invoking", false);
    try {
      Role[] roles = null;
      String[] roleStrs = args[0].split(",");

      roles = new Role[roleStrs.length];

      for (int i = 0; i < roles.length; i++) {
        String name = roleStrs[i].trim();
        if (name.length() == 0) {
          continue;
        }
        roles[i] = Role.valueOf(name);
      }
      WSServer server = new WSServer(new LinkedList<Participant.Role>(Arrays.asList(roles)), Integer.parseInt(args[1].trim()), args[2].trim().equals("屠边"), Integer.valueOf(args[3]));

      server.start();
      return server;
    } catch (Exception e) {
      e.printStackTrace();
      LogUtils.logWarningString("启动失败. 命令行格式: 英文逗号分隔的角色   屠边/屠城   端口", "服务器", false);
      return null;
    }
  }
}
